﻿#include <iostream>
#include <vector>
#include <array> 
#include <functional> 

//传递不同元素类型的std::vector
void passByRef(const std::vector<int>& arr)
{
	std::cout << arr[0] << '\n';
}

//使用函数模板传递不同元素类型或长度的std::array
template <typename T, std::size_t N> // 注意这里模版参数声明与 std::array 一致
void passByRef(const std::array<T, N>& arr)
{
	static_assert(N != 0); // 如果 std::array 为空，则编译失败

	std::cout << arr[0] << '\n';
}


//定义并分配结构体给std::array
struct House
{
	int number{};
	int stories{};
	int roomsPerStory{};
};




int main()
{
	std::cout << "容器和数组的演示：\n";

	std::string name{ "字符串 是 字母 的容器" }; // 字符串 是 字母 的容器
	std::cout << name << " \n"; // 将字符串按字母打印

	//容器中元素的数量通常称为它的长度（有时也称 大小）。
	std::cout << "容器中元素的数量=" << name.length() << " \n";

	//vector是C++标准容器库中实现数组的容器类之一
	std::vector<int> em{ 1,2,3 };
	std::cout << em[0] << " \n"; //通过下标访问元素,使用运算符[]访问数组元素不进行边界检查

	//数组的定义特征之一是元素总是在内存中连续分配，
	// 这意味着元素在内存中都是相邻的（它们之间没有间隙）
	std::cout << &(em[0]) << '\n';
	std::cout << &(em[1]) << '\n';
	std::cout << &(em[2]) << '\n';

	//使用size()成员函数或std::size()获取std::vector的长度
	std::cout << "容器em中元素的数量=" << em.size() << " \n";


	//使用at()成员函数访问数组元素执行运行时边界检查
	std::cout << em.at(2); // 打印下标为3的位置中存储的值 (7)
	//std::cout << em.at(9); // 无效索引 (抛出异常)

	passByRef(em);

	//调整大小
	em.resize(10);
	//遍历输出
	for (auto i : em)  std::cout << i << ' ';
	std::cout << "容量: " << em.capacity() << " 长度:" << em.size() << '\n';

	//resize重新分配通常很昂贵。避免不必要的重新分配。
	//vector索引基于长度，而不是容量


	//使用std::vector的栈行为
	em.push_back(99); // push_back() 就一个元素推入栈中
	//遍历输出
	for (auto i : em)  std::cout << i << ' ';
	std::cout << "Top: " << em.back() << '\n'; // back() 返回最后一个元素

	em.pop_back(); // pop_back() 移除栈最后一个元素
	//遍历输出
	for (auto i : em)  std::cout << i << ' ';
	std::cout << '\n'; 

	//与下标运算符[]或at()成员函数不同，
	// push_back()（和emplace_back）将增加vector的长度，
	// 并在容量不足以插入值时导致重新分配。

	/*分配的额外容量取决于编译器对std::vector的实现，不同的编译器通常会执行不同的操作:
	GCC和Clang使当前容量加倍。当触发最后一次调整大小时，容量将从2倍增加到4倍。
	Visual Studio 2022将当前容量乘以1.5。触发最后一次调整大小时，容量从2更改为3。*/

	//调用reserve更改容量
	em.reserve(10);
	//遍历输出
	for (auto i : em)  std::cout << i << ' ';
	std::cout << '\n';


	//数组
	std::vector<int> b(5);    // 5个int的 std::vector
	std::array<int,6> fibonnaci = { 0, 1, 1, 2, 3, 5 }; // 使用大括号列表的拷贝初始化
	std::array<int, 5> prime{ 2, 3, 5, 7, 11 };         // 使用大括号的列表初始化 (推荐使用)
	
	std::cout << "fibonnaci length: " << fibonnaci.size() << '\n';
	std::cout << "prime length: " << prime.size() << '\n';

	std::cout << std::get<3>(prime); // 打印索引 3 位置的元素
	//std::cout << std::get<9>(prime); // 无效索引 (编译失败)


	std::array arr{ 9, 7, 5, 3, 1 }; // 使用 CTAD 推导类型为 std::array<int, 5>
	passByRef(arr);  // ok: 编译器会实例化 passByRef(const std::array<int, 5>& arr)

	std::array arr2{ 1, 2, 3, 4, 5, 6 }; // 使用 CTAD 推导类型为 std::array<int, 6>
	passByRef(arr2); // ok: 编译器会实例化 passByRef(const std::array<int, 6>& arr)

	std::array arr3{ 1.2, 3.4, 5.6, 7.8, 9.9 }; // 使用 CTAD 推导类型为 std::array<double, 5>
	passByRef(arr3); // ok: 编译器会实例化 passByRef(const std::array<double, 5>& arr)




	//定义House的std::array并分配元素
	std::array<House, 3> houses{};

	houses[0] = { 13, 1, 7 };
	houses[1] = { 14, 2, 5 };
	houses[2] = { 15, 2, 4 };

	for (const auto& house : houses)
	{
		std::cout << "House number " << house.number
			<< " has " << (house.stories * house.roomsPerStory)
			<< " rooms.\n";
	}






	int x{ 1 };
	int y{ 2 };
	int z{ 3 };
	//通过std::reference_wrapper创建引用的数组
	std::array<std::reference_wrapper<int>, 3> arrw{ x, y, z };
	arrw[1].get() = 5; // 修改下标 1 处的值
	std::cout << arrw[1] << y << '\n'; // 修改了 arr[1] 和 y, 打印 55



	//指针运算是一种功能，允许我们将某些整数算术运算符
	// （加法、减法，自增，自减）应用于指针，以产生新的内存地址。
	const int* ptr{ &x }; // 假设 int 是 4 字节
	std::cout << ptr << ' ' << (ptr + 1) << ' ' << (ptr + 2) << '\n';

	//通过指针算法实现数组访问
	const int arrp[]{ 9, 7, 5, 3, 1 };
	const int* ptrp{ arrp }; // 保存数组初始元素的地址
	std::cout << ptrp[2];   // 访问第 2 个元素, 打印 5


	//二位数组
	int arr22[3][4]{
	   { 1, 2, 3, 4 },
	   { 5, 6, 7, 8 },
	   { 9, 10, 11, 12 } };

	// 使用长度的两层for循环
	for (std::size_t row{ 0 }; row < std::size(arr22); ++row) // std::size(arr) 返回行的个数
	{
		for (std::size_t col{ 0 }; col < std::size(arr22[0]); ++col) // std::size(arr[0]) 返回列的个数
			std::cout << arr22[row][col] << ' ';

		std::cout << '\n';
	}







}
